#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <map>
#include <iostream>
#include <fstream>

#include "iconv.h"
#include "png.h"
#include "tinyxml.h"

#include "swf.h"
#include "SWFHeader.h"
#include "PlaceObject2.h"
#include "SWFHeader.h"
#include "TextRecord.h"
#include "DefineText2.h"
#include "DefineFont2.h"
#include "DefineBitsLossless.h"
#include "DefineShape3.h"
#include "DefineShape.h"
#include "Bits.h"
#include "CSMTextSettings.h"
#include "SetBackgroundColor.h"
#include "ShowFrame.h"
#include "RGBA.h"

using namespace std;
using namespace swfstream;
SWFHeader *header;
const char* swf_name;
//FILE*html;
const int n = 1;
std::vector<Tag*> *tags = new std::vector<Tag*>;
Dictionary dictionary;

class DisplayObject {
public:
	uint left;
	uint height;
	uint depth;
	Defination*data;
	PlaceObject2* attributes;
};

class PlayList: public std::map<uint, DisplayObject*> {
public:
	RGB* background_color;
	void to_html(const char*fname) {
		FILE* html = fopen(fname, "w");
		fprintf(
				html,
				"%s",
				"<html>\n<link rel=\"stylesheet\" type=\"text/css\" href=\"a.css\" />\t<body>\n\t\t");
		fprintf(html, "<table border=1 width=%d height=%d",
				(uint) header->frame_size->x_max / 20 / n,
				(uint) header->frame_size->y_max / 20 / n);
		if (background_color != NULL) {
			fprintf(html, " style=\"background-color:rgb(%u,%u,%u)\"",
					(uint) background_color->r, (uint) background_color->g,
					(uint) background_color->b);
		}
		fprintf(html, ">\n\t\t\t<td>\n</td></table>");
		for (uint i = 0; i < 10000; i++) {
			if ((*this)[i] == NULL) {
				continue;
			}
			cout << "depth:" << i << ",type:" << (*this)[i]->data->header->type
					<< endl;
			//(*this)[i]->data->print();
			if ((*this)[i]->data->header->type == TAG_TYPE_DEFINE_TEXT2) {
//					puts("!!!!!!!define text2");
				TextRecord att;
				att.StyleFlagsHasFont = false;
				att.StyleFlagsHasColor = false;
				att.StyleFlagsHasXOffset = true;
				att.StyleFlagsHasYOffset = true;
				att.XOffset = 0;
				att.YOffset = 0;
				DefineText2* tag = (DefineText2*) (*this)[i]->data;
				Point p;
				p.x = tag->TextBounds->x_min;
				p.y = tag->TextBounds->y_min;
				//point q=tag->TextMatrix->trans(p);
				Point q = p;

				fprintf(
						html,
						"<!--define_text2--><span border=1 class=\"define_text2\" style=\"position: absolute;width:%d;height:%d;left:%d;top:%d;\"></span>\n",
						(int) ((tag->TextBounds->x_max - tag->TextBounds->x_min)
								/ 20 / n),
						(int)((tag->TextBounds->y_max-tag->TextBounds->y_min)/20/n),
						(int)(q.x/20/n),
						(int)(q.y/20/n)
						);
				///text records
				for (uint j = 0; j < tag->TextRecords->size(); j++) {
					TextRecord* record = tag->TextRecords->at(j);
					///read the att defined by the record
					if (record->StyleFlagsHasFont) {
						att.StyleFlagsHasFont = true;
						att.FontID = record->FontID;
						att.TextHeight = record->TextHeight;
					}
					if (record->StyleFlagsHasColor) {
						att.StyleFlagsHasColor = true;
						att.TextColor = record->TextColor;
					}
					if (record->StyleFlagsHasXOffset) {
						att.StyleFlagsHasXOffset = true;
						att.XOffset = record->XOffset;
					} else {
						//att.StyleFlagsHasXOffset=false;
					}
					if (record->StyleFlagsHasYOffset) {
						att.StyleFlagsHasYOffset = true;
						att.YOffset = record->YOffset;
					} else {
						//att.StyleFlagsHasYOffset=false;
					}
					if (!att.StyleFlagsHasFont) {
						fprintf(stderr, "drawing:no font ,not finished");
						exit(5);
					}
					DefineFont2*font;
					//font=dynamic_cast<define_font2*>(dictionary[tag->TextRecords->at(j)->FontID]);
					//font=(define_font2*)(dictionary[tag->TextRecords->at(j)->FontID]);
					font = (DefineFont2*) (dictionary[att.FontID]);
//						cout<<"fontid:"<<tag->TextRecords->at(j)->FontID<<",font:"<<font<<endl;
//						cout<<tag->TextRecords->size()<<"::::"<<j<<endl;
					fprintf(html, "\t<span class=\"text_record\" style=\"");
					if (att.StyleFlagsHasFont) {
						fprintf(html, "font-size:%u;",
								(uint) ((uint) att.TextHeight / n));
					}
					if (att.StyleFlagsHasColor) {
						//fprintf(stderr,"not done");
						//exit(1);
					}
					if (att.StyleFlagsHasXOffset || att.StyleFlagsHasYOffset) {
						fprintf(html, "position: absolute;");
					}
					float x = 0, y = 0;
					Matrix* m = tag->TextMatrix;
					Point p;
					p.x = att.XOffset;
					p.y = att.YOffset;
					Point q = tag->TextMatrix->trans(p);
					fprintf(html, "left:%d;", (int) (q.x / 20 / n));
					fprintf(html, "top:%d;", (int) (q.y / 20 / n));
					int left = 0;
					fprintf(html, "\"><!--text_record-->");
					//record->print();
					//printf("record:");
					for (uint i = 0; i < record->GlyphEntries->size(); i++) {
						uint index = record->GlyphEntries->at(i)->GlyphIndex;
						void* codes = font->CodeTable;
						//to gbk
						if (font->FontFlagsWideCodes) {
							iconv_t cd = iconv_open("GBK", "UCS-2LE");
							int16 in_buf = *(((uint16*) codes) + index);
							char* in = (char*) &in_buf;
							size_t in_left = 2;
							size_t out_left = 7;
							char* out = new char[10];
							for (int i = 0; i < 10; i++) {
								out[i] = '\0';
							}
							char*outbuf = out;
							size_t iconv_r = iconv(cd, &in, &in_left, &outbuf,
									&out_left);
							//out[out_left]='\0';
							fprintf(html, "<span style=\"position:absolute;");
							fprintf(html, "left:%d;", (int) left);
							left += record->GlyphEntries->at(i)->GlyphAdvance
									/ 20 / n;

							fprintf(html, "\">");
							fprintf(html, "%s", out);
							//printf("%s",out);
							fprintf(html, "</span>\n");
						} else {
							exit(3);
						}
					}
					//fprintf(html,"</td></table><!--text_record done-->\n");
					putchar('\n');
					fprintf(html, "</span><!--text_record done-->\n");
				}
				fprintf(html, "<!--define_text2 done-->\n");
			} else if (((*this)[i])->data->header->type
					== TAG_TYPE_DEFINE_BITS_LOSSLESS) {
				DefineBitsLossless*tag =
						(DefineBitsLossless*) ((*this)[i]->data);
				tag->to_png("rrr.png");
			} else if (((*this)[i])->data->header->type
					== TAG_TYPE_DEFINE_SHAPE3) {
				DefineShape3* tag = (DefineShape3*) ((*this)[i]->data);
				char fn[1000];
				//extern char* swf_name;
				//sprintf(fn,"%s_t%u.svg",swf_name,(uint)tag->CharacterID);
				sprintf(fn, "%s_t%u.png", swf_name, (uint) tag->CharacterID);
				cerr << "!!!!!!!call svn" << endl;
				tag->to_svg(fn);
				cerr << "!!!!!!!done svn" << endl;
				//fprintf(html,"<object type=\"image/svg+xml\" data=\"%s\" style=\"position:absolute;left:0;top:0;\"></object>>",fn);
				fprintf(
						html,
						"<img src=\"%s\" style=\"position:absolute;left:0;top:10;\"></img>",
						fn);
			} else if (((*this)[i])->data->header->type
					== TAG_TYPE_DEFINE_SHAPE) {
				DefineShape* tag = (DefineShape*) ((*this)[i]->data);
				char fn[1000];
				//extern char* swf_name;
				//sprintf(fn,"%s_t%u.svg",swf_name,(uint)tag->CharacterID);
				sprintf(fn, "%s_shape1_%u.png", swf_name,
						(uint) tag->CharacterID);
				cerr << "!!!!!!!call svn" << endl;
				tag->to_pic(fn);
				cerr << "!!!!!!!done svn" << endl;
				//fprintf(html,"<object type=\"image/svg+xml\" data=\"%s\" style=\"position:absolute;left:0;top:0;\"></object>>",fn);
				uint width = tag->shape_bounds->x_max
						- tag->shape_bounds->x_min;
				uint height = tag->shape_bounds->y_max
						- tag->shape_bounds->y_min;
				fprintf(
						html,
						"<img src=\"%s\" style=\"position:absolute;left:0;top:10;width:%u;height:%u;\"></img>",
						fn, width / 20, height / 20);
			}
		}
		fprintf(html, "%s", "\t\t\t\n\t</body>\n</html>");
		fclose(html);
	}
};

static PlayList play_list;

void TagHeader::print() {
	cerr << "tag_header::length:" << (uint) length << ",type:" << (uint) type
			<< endl;
//	printf("tag_header::length:%u,type:%u\n",(uint)length,(uint)type);
}
GlyphEntry* GlyphEntry::from_file(FILE* f, int GlyphBits, int AdvanceBits) {
	fprintf(stderr, "do not use this\n");
	exit(1);
	puts("reading GLYPHENTRY");
	GlyphEntry *r = new GlyphEntry;
	Bits bits;
	r->GlyphIndex = bits.read_u(GlyphBits, f);
	r->GlyphAdvance = bits.read_s(AdvanceBits, f);
	printf("GlyphIndex:%u,GlyphAdvance:%d\n", (uint) r->GlyphIndex,
			(int) r->GlyphAdvance);
	return r;
}
std::vector<GlyphEntry*>* GlyphEntry::list_from_file(FILE* f, int GlyphBits,
		int AdvanceBits, int count) {
	std::vector<GlyphEntry*>*rr = new std::vector<GlyphEntry*>;
	//puts("reading GLYPHENTRY[%d]");
	Bits bits;
	for (int i = 0; i < count; i++) {
		GlyphEntry *r = new GlyphEntry;
		r->GlyphIndex = bits.read_u(GlyphBits, f);
		r->GlyphAdvance = bits.read_s(AdvanceBits, f);
		//printf("GlyphIndex:%u,GlyphAdvance:%d\n",(uint)r->GlyphIndex,(int)r->GlyphAdvance);
		rr->push_back(r);
	}
	return rr;
}

CSMTextSettings* CSMTextSettings::from_file(FILE* f) {
	//puts("reading GSMTextSettings");
	CSMTextSettings *r = new CSMTextSettings;
	fread(&r->TextID, 2, 1, f);
	//reset_bits();
	Bits bits;
	r->UseFlashType = bits.read_u(2, f);
	r->GridFit = bits.read_u(3, f);
	//	r.Reserved=read_bits(3,f);
	if (bits.read_u(3, f) != 0) {
		fprintf(stderr, "Reserved must be set to 0\n");
		exit(1);
	}
	//reset_bits();
	fread(&r->Thickness, 4, 1, f);
	fread(&r->Sharpness, 4, 1, f);
	//	fread(&r.Reserved2,1,1,f);
	if (fgetc(f) != 0) {
		fprintf(stderr, "Reserved must be set to 0\n");
		exit(1);
	}
	return r;
}
#include "SWFInputStream.h"
int main(int argc, char** argv) {
	if (argc != 2) {
		cerr << "usage:" << argv[0] << " swf_file\n";
		exit(1);
	}
//	freopen("out.txt","w",stdout);
//	FILE* f=fopen(argv[1],"rb");
	std::ifstream fs(argv[1], std::ifstream::binary);
	SWFInputStream swf(&fs);
	swf_name = argv[1];

//	header=SWFHeader::from_file(f);
	cerr << header << endl;
//	header->print();
	swf.get(header);
//	cerr<<header<<endl;
//	header->print();
//	exit(1);
	int tag_count = 0;
//	while(!feof(f)){
	int ii = 0;
	while (1) {
		ii++;
		if (ii > 500)
			break;
		Tag* tag;
		swf.get(tag);
		cerr << "--tag==" << tag << endl;
		if (swf.bytes_read > header->file_length) {
			cerr << "break size:" << swf.bytes_read << ":"
					<< header->file_length << endl;
			//break;
		}
//		cerr<<"$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$"<<endl;
		if (tag == NULL) {
//			cerr<<"if$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$"<<endl;
			cerr << "tag is null,break" << endl;
			break;
		} else {
			tag_count++;
			cerr << "--tag_count=" << tag_count << endl;
			cerr << "--tag.print()=" << endl;
			//tag->print();
//			tag->header->print();
//			cerr<<"else$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$"<<endl;
			tags->push_back(tag);
			continue;
			if (dynamic_cast<Defination*>(tag)) {
				Defination* def = (Defination*) tag;
				uint16 id = ((Defination*) tag)->CharacterID;
				dictionary[id] = def;
				cout << "new id:" << id << endl;
				//printf("pushed,size=%u\n",(uint)dictionary.size());
			} else if (dynamic_cast<PlayListTag*>(tag)) {
				PlayListTag*tg = (PlayListTag*) tag;
				if (dynamic_cast<PlaceObject2*>(tag)) {
					PlaceObject2& po = *(PlaceObject2*) tag;
					if (po.PlaceFlagHasClipActions) {
						cerr << "PlaceFlagHasClipActions,not finished" << endl;
						exit(22);
					}
					DisplayObject* obj = NULL;
					if ((!po.PlaceFlagMove) && po.PlaceFlagHasCharacter) {
						obj = new DisplayObject;
						obj->data = dictionary[po.characterId];
						obj->attributes = &po;
						play_list[po.depth] = obj; //add
					} else if (po.PlaceFlagMove
							&& (!po.PlaceFlagHasCharacter)) {
						obj = play_list[po.depth];
					} else if (po.PlaceFlagMove && po.PlaceFlagHasCharacter) {
						obj = new DisplayObject;
						obj->data = dictionary[po.characterId];
						obj->attributes = &po;
						play_list[po.depth] = obj; //change delete?
					} else {
						cerr << "not finished" << endl;
						//throw;
						exit(22);
					}
				}
			} else if (dynamic_cast<SetBackgroundColor*>(tag)) {
				play_list.background_color =
						((SetBackgroundColor*) tag)->background_color;
			} else if (dynamic_cast<ShowFrame*>(tag)) {
				char html_name[1000];
				sprintf(html_name, "%s.htm", swf_name);
				//	play_list.to_html(html_name);
				break;
				//exit(33);
				//display
				//throw;
			}
		}
	}
//	printf("tag_count=%d\n",tag_count-1);
//	char html_name[1000];
//	sprintf(html_name,"%s.htm",swf_name);
//	play_list.to_html(html_name);
	TiXmlDocument *doc = new TiXmlDocument;
	TiXmlElement *root = new TiXmlElement("root");
	doc->LinkEndChild(root);
	root->LinkEndChild(header->toTiXmlElement());
	for (int i = 0; i < tags->size(); i++) {
		root->LinkEndChild(tags->at(i)->toTiXmlElement());
	}
	doc->SaveFile("out.xml");
	cerr << "!!--main ends--!!" << dec << tag_count << "tags," << swf.bytes_read
			<< "bytes" << endl;
	//fflush(stdout);
	return 0;
}
void ShapeWithStyle::print() {
	FillStyles->print();
	LineStyles->print();
	cout << "ShapeWithStyle::NumFillBits:" << (uint) NumFillBits
			<< ",NumLineBits:" << (uint) NumLineBits << "\n";
	cout << "record_num:" << ShapeRecords->size() << "\n";
	for (uint i = 0; i < ShapeRecords->size(); i++) {
		ShapeRecords->at(i)->print();
	}
}
ShapeWithStyle* ShapeWithStyle::from_file(FILE* f, int shape_version) {
	long s = ftell(f);
	ShapeWithStyle* r = new ShapeWithStyle;
	r->FillStyles = FillStyleArray::from_file(f, shape_version);
	r->LineStyles = LineStyleArray::from_file(f, shape_version);
	byte tmp = getc(f);
	r->NumFillBits = ((tmp & 0xf0) >> 4);
	r->NumLineBits = (tmp & 0x0f);
	r->ShapeRecords = ShapeRecord::list_from_file(f, r->NumFillBits,
			r->NumLineBits);
	//puts("shape_with_style done");
	//exit(12);
	//printf("length of shape_with_style:%ld\n",ftell(f)-s);
	return r;
}
void FillStyleArray::print() {
	cout << "FILLSTYLEARRAY::FillStyleCount:" << (uint) FillStyleCount
			<< ",FillStyleCountExtended:" << (uint) FillStyleCountExtended
			<< "\n";
	for (uint i = 0; i < size(); i++) {
		at(i)->print();
	}
}
void LineStyleArray::print() {
	cout << "LINESTYLEARRAY::LineStyleCount:" << (uint) LineStyleCount
			<< ",LineStyleCountExtended:" << (uint) LineStyleCountExtended
			<< "\n";
	for (uint i = 0; i < size(); i++) {
		at(i)->print();
	}
}
FillStyleArray* FillStyleArray::from_file(FILE* f, int shape_version) {
	long s = ftell(f);
	uint size;
	FillStyleArray* r = new FillStyleArray;
	//r->FillStyleCount=getc(f);
	//size=r->FillStyleCount;
	size = getc(f);
	//printf("FILLSTYLEARRAY.size1=%u\n",size);
	//	if((r->FillStyleCount==0xff)&&((shape_version==2)||(shape_version==3))){
	if ((size == 0xff) && ((shape_version == 2) || (shape_version == 3))) {
		//exit(16);
		//fread(&r->FillStyleCountExtended,2,1,f);
		//size=r->FillStyleCountExtended;
		uint16 tmp;
		fread(&tmp, 2, 1, f);
		size = tmp;
		//printf("FILLSTYLEARRAY.size2=%u\n",size);
	}
	for (uint i = 0; i < size; i++) {
		r->push_back(FillStyle::from_file(f, shape_version));
	}
	//	r->FillStyles=new fill_style*[r->FillStyleCount];
	//	for(int i=0;i<r->FillStyleCount;i++){
	//		r->FillStyles[i]=fill_style::from_file(f);
	//	}
	//printf("length of FILLSTYLEARRAY:%ld\n",ftell(f)-s);
	return r;
}
LineStyleArray* LineStyleArray::from_file(FILE* f, int shape_version) {
	long s = ftell(f);
	uint size;
	LineStyleArray* r = new LineStyleArray;
	//r->FillStyleCount=getc(f);
	//size=r->FillStyleCount;
	uint tmp = getc(f);
	r->LineStyleCount = tmp;
	//printf("LINESTYLEARRAY.size1=%u\n",(uint)tmp);
	if (tmp == 0xff) {
		//exit(16);
		uint16 tmp2;
		fread(&tmp2, 2, 1, f);
		size = tmp2;
		r->LineStyleCountExtended = size;
		//printf("LINESTYLEARRAY.size2=%u\n",size);
	} else {
		size = tmp;
	}
	for (uint i = 0; i < size; i++) {
		if (shape_version <= 3) {
			r->push_back(LineStyle::from_file(f, shape_version));
		} else {
			fprintf(stderr, "in line_style_array::from_file,not finished\n");
			exit(1);
		}
	}
	//	r->FillStyles=new fill_style*[r->FillStyleCount];
	//	for(int i=0;i<r->FillStyleCount;i++){
	//		r->FillStyles[i]=fill_style::from_file(f);
	//	}
	//printf("length of LINESTYLEARRAY:%ld\n",ftell(f)-s);
	return r;
}
FillStyle* FillStyle::from_file(FILE* f, int shape_version) {
	long s = ftell(f);
	FillStyle* r = new FillStyle;
	r->FillStyleType = getc(f);
	//fprintf(stderr,"FillStyleType=0x%X\n",r->FillStyleType);
	if (r->FillStyleType == 0) {
		if ((shape_version == 1) || (shape_version == 2)) {
			r->color = RGB::from_file(f);
			//puts("rgb");
		} else if (shape_version == 3) {
			r->color = RGBA::from_file(f);
			//puts("rgba");
		} else {
			fprintf(stderr, "in fill_style::from_file,not finished\n");
			exit(1);
		}
	}
	if ((r->FillStyleType == 0x10) || (r->FillStyleType == 0x12)
			|| (r->FillStyleType == 0x13)) {
		r->GradientMatrix = Matrix::from_file(f);
	}
	if ((r->FillStyleType == 0x10) || (r->FillStyleType == 0x12)
			|| (r->FillStyleType == 0x13)) {
		fprintf(stderr, "in fill_style::from_file,type=0x%X,not finished\n",
				(uint) r->FillStyleType);
		exit(1);
	}
	if ((r->FillStyleType == 0x40) || (r->FillStyleType == 0x41)
			|| (r->FillStyleType == 0x42) || (r->FillStyleType == 0x43)) {
		uint16 tmp;
		fread(&tmp, 2, 1, f);
		r->BitmapId = tmp;
		//printf("bmpid=%u\n",(uint)tmp);
		(r->BitmapMatrix = Matrix::from_file(f))->print();
		//fprintf(stderr,"in fill_style::from_file,type=0x%X,not finished\n",(uint)r->FillStyleType);
		//exit(1);
	}
	//printf("FillStyle done\n");
	//	exit(3);
	//printf("length of fill_style:%ld\n",ftell(f)-s);
	return r;
}
void FillStyle::print() {
	cout << "fill_style::type:" << hex << (uint) FillStyleType << "\n" << dec;
	if (FillStyleType == 0) {
		cout << "color:";
		color->print();
	} else if (FillStyleType == 0x41) {
		cout << "BitmapId:" << BitmapId << "\n";
		cout << "BitmapMatrix:";
		BitmapMatrix->print();
		//exit(36);
	} else {
		cerr << "fillstyle::print not finished\n";
		exit(1);
	}
}
void LineStyle::print() {
	cout << "line_style::width:" << width << ",color:";
	color->print();
}
LineStyle* LineStyle::from_file(FILE* f, int shape_version) {
	long s = ftell(f);
	LineStyle* r = new LineStyle;
	uint16 tmp;
	fread(&tmp, 2, 1, f);
	r->width = tmp;
	if ((shape_version == 1) || (shape_version == 2)) {
		r->color = RGB::from_file(f);
	} else if (shape_version == 3) {
		r->color = RGBA::from_file(f);
	} else {
		fprintf(stderr, "in line_style::from_file:not finished");
		exit(1);
	}
	//printf("length of line_style:%ld\n",ftell(f)-s);
	return r;
}
void CSMTextSettings::print() {
	printf(
			"CSMTextSettings::TextID:%u,UseFlashType:%u,GridFit:%u,Thickness:%f,Sharpness:%f\n",
			TextID, UseFlashType, GridFit, Thickness, Sharpness);
}
void GlyphEntry::print() {
	printf("GLYPHENTRY::GlyphIndex:%u,GlyphAdvance:%d\n", (uint) GlyphIndex,
			(int) GlyphAdvance);
}

